﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Hont
{
    public class ATAsync
    {
        public class TaskAttachInfo
        {
            public IATTask Task;
            public bool IsExecuteInvoked;
            public bool IsExecuteFinished;

            public void TryExecute()
            {
                if (!IsExecuteInvoked) Task.Execute();
                IsExecuteInvoked = true;
            }
        }

        public class AsyncOperationInfo
        {
            public List<TaskAttachInfo> TasksList;
            public Action AsyncOp;


            public AsyncOperationInfo()
            {
                TasksList = new List<TaskAttachInfo>();
            }
        }

        AsyncOperationInfo mTmpAsyncOperation;
        Queue<AsyncOperationInfo> mAsyncOperationQueue;


        public ATAsync()
        {
            mTmpAsyncOperation = new AsyncOperationInfo();
            mAsyncOperationQueue = new Queue<AsyncOperationInfo>();
        }

        public void Await(IATTask context)
        {
            var taskResult = new TaskAttachInfo();
            taskResult.Task = context;

            context.OnTriggerResult += () =>
            {
                taskResult.IsExecuteFinished = true;
                ExecuteDetection();
            };

            mTmpAsyncOperation.TasksList.Add(taskResult);
            ExecuteDetection();
        }

        public AsyncValue<T> Await<T>(IATTask context)
        {
            var taskResult = new TaskAttachInfo();
            taskResult.Task = context;

            context.OnTriggerResult += () =>
            {
                taskResult.IsExecuteFinished = true;
                ExecuteDetection();
            };

            mTmpAsyncOperation.TasksList.Add(taskResult);
            ExecuteDetection();

            return (AsyncValue<T>)context.AsyncReturnValue;
        }

        /// <summary>
        /// Write async code.
        /// </summary>
        /// <param name="context"></param>
        public void Async(Action context)
        {
            var newAsyncOperation = new AsyncOperationInfo() { AsyncOp = context, TasksList = mTmpAsyncOperation.TasksList };
            mAsyncOperationQueue.Enqueue(newAsyncOperation);
            mTmpAsyncOperation = new AsyncOperationInfo();
            ExecuteDetection();
        }

        /// <summary>
        /// Clear all async operations.
        /// </summary>
        public void Clear()
        {
            mTmpAsyncOperation = new AsyncOperationInfo();
            mAsyncOperationQueue.Clear();
        }

        /// <summary>
        /// Detection async info is completed, not manual invoke, unless not callback.
        /// </summary>
        public void ExecuteDetection()
        {
            if (mAsyncOperationQueue.Count == 0) return;

            if (TaskExecuteDetection(mAsyncOperationQueue.Peek()))
            {
                var item = mAsyncOperationQueue.Dequeue();
                item.AsyncOp();
                item.TasksList.Clear();

                ExecuteDetection();
            }
        }

        bool TaskExecuteDetection(AsyncOperationInfo asyncOp)
        {
            bool result = false;

            if (asyncOp.TasksList.Count > 0 && !asyncOp.TasksList[0].IsExecuteFinished)
                asyncOp.TasksList[0].TryExecute();

            for (int i = 0; i < asyncOp.TasksList.Count; i++)
            {
                if (asyncOp.TasksList[i].IsExecuteFinished
                    && asyncOp.TasksList.Count > i + 1
                    && !asyncOp.TasksList[i + 1].IsExecuteFinished)
                {
                    asyncOp.TasksList[i + 1].TryExecute();
                }
            }

            if (asyncOp.TasksList.All(m => m.IsExecuteFinished))
            {
                result = true;
            }

            return result;
        }
    }
}